
//
// 101101 - AH - Created.
// 101217 - AH - Moved from org.soekrat.swing
// 110112 - AH - For unknown reason normal link method does not work from here
//               after Network started to use listenable graph so must use
//               _Hack_link, hope to go back to link later!
// 110529 - AH - (Victoria) getIndex(NetworkNode).
// 110610 - AH - (Brann Stadion *) Made node names editable in table.
// 110618 - AH - (Bybanen *) Made setValueAt fire change event so new node
//               name is also update in column heading.
//

package org.soekrat.swing.network;

import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

import org.soekrat.engine.network.Network;
import org.soekrat.engine.network.NetworkNode;
import org.soekrat.engine.util.Util;

/**
 * <p>
 *   Adapts a 
 *   {@link Network} to a 
 *   {@link JTable}.
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC) 
 */
@SuppressWarnings("serial")
public class NetworkTableAdaptor extends AbstractTableModel 
{
  private final Network m_network;
  
  private NetworkNode[] m_nodes;
  
  /**
   * <p>
   *   Creates with an empty
   *   {@link Network}. 
   * </p>
   */
  public NetworkTableAdaptor()
  { 
    m_network=new Network(); 
    m_nodes=NetworkNode.E_ARRAY; 
  }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param network {@link Network} to adapt.
   */
  public NetworkTableAdaptor(Network network) 
  {
    if (network==null)
    {
      throw new NullPointerException("network");
    }
    
    m_network=network;
    m_nodes=network.getNodes();
  }
  
  /**
   * <p>
   *   Gets index of node.
   * </p>
   * @param node Node to get index for.
   * @return Index or {@code -1} if not found.
   */
  public int getIndex(NetworkNode node)
  {
    if (node==null)
    {
      throw new NullPointerException("node");
    }
    
    return Util.getIndexOf(m_nodes, node);
  }
  
  /**
   * <p>
   *   Gets
   *   {@link Network} viewed.
   * </p>
   * @return {@link Network}.
   */
  public Network getNetwork(){ return m_network; }

  // Specified in TableModel.
  public Class<?> getColumnClass(int col)
  { 
    return String.class; 
  }

  // Specified in TableModel.
  public String getColumnName(int columnIndex) 
  {
    if (columnIndex==0) return "Nodes";
    return m_nodes[columnIndex-1].toString();
  }

  // Specified in TableModel.
  public int getRowCount(){ return m_nodes.length; }
  
  // Specified in TableModel.
  public int getColumnCount(){ return m_nodes.length+1; }
  
  //Specified in TableModel.
  public boolean isCellEditable(int row, int col){ return col==0; }

  // Specified in TableMode.
  public Object getValueAt(int row, int col)
  { 
    if (col==0)
    {
      return m_nodes[row].getSoekratName().toString();
    }
    else
    {
      int c=col-1;
      
      if (row==c) return "-";
      
      NetworkNode a=m_nodes[row];
      NetworkNode b=m_nodes[c];
      boolean v=m_network.hasLink(a, b);
    
      if (v==true) return "1";
      else         return "0";
    }
  }
  
  @Override
  public void setValueAt(Object val, int row, int cal)
  {
    String name=(val==null) ? "" : val.toString();
    name=(name==null) ? "" : name.trim();
    if (name.length()>0)
    {
      NetworkNode node=m_nodes[row];
      node.setName(name);
      fireTableStructureChanged();
    }
  }

  /**
   * <p>
   *   Gets number of nodes in 
   *   {@link Network}.
   * </p>
   * @return Count.
   */
  public int getNodeCount(){ return m_nodes.length; }
  
  /**
   * <p>
   *   Gets node.
   * </p>
   * @param idx Node's index.
   * @return {@link NetworkNode}.
   * @throws IndexOutOfBoundsException If {@code idx<0 || idx>=getNodeCount()}.
   */
  public NetworkNode getNode(int idx){ return m_nodes[idx]; }
  
  /**
   * <p>
   *   Removes node.
   * </p>
   * @param node {@link NetworkNode} to remove.
   * @return {@code true} if {@code node} removed, {@code false} if {@code node}
   *         not found.
   */
  public boolean removeNode(NetworkNode node)
  {
    if (node==null)
    {
      throw new NullPointerException("node");
    }
    
    if (m_network.removeNode(node))
    {
      m_nodes=m_network.getNodes();
      fireTableStructureChanged();
      return true;
    }
    return false;
  }
  
  /**
   * <p>
   *   Creates a new
   *   {@link NetworkNode}.
   * </p>
   * @param name Name associated with new node.
   * @return Created or {@code null} if already had node named {@code name}.
   */
  public NetworkNode createNetworkNode(String name)
  {
    if (name==null)
    {
      throw new NullPointerException("name");
    }
    
    if (m_network.hasNamedNode(name)) return null;
    
    NetworkNode retVal=m_network.createNode(name);
    m_nodes=m_network.getNodes();
    
    fireTableStructureChanged();
    
    return retVal;
  }
  
  /**
   * <p>
   *   Creates a new
   *   {@link NetworkNode}.
   * </p>
   * @return Created.
   */
  public NetworkNode createNetworkNode()
  {
    NetworkNode retVal=m_network.createNode();
    m_nodes=m_network.getNodes();
    
    fireTableStructureChanged();
    
    return retVal;
  }
  
  /**
   * <p>
   *   Handles a cell selection done by the end user.
   * </p>
   * <p>
   *   Currently this does toggle the relationship.
   * </p>
   * @param row Row index of cell selected.
   * @param col Column index of cell selected.
   */
  public void handleCellSelection(int row, int col)
  {
    if (col>0)
    {
      col--;
      
      if (col==row) return;
      
      NetworkNode left=m_nodes[row];
      NetworkNode right=m_nodes[col];
      
      if (m_network.hasLink(left, right)) m_network.unlink(left, right);  
      else                                m_network._Hack_link(left, right);
      
      fireTableCellUpdated(row, col);
    }
  }
  
}
